<!DOCTYPE html><html lang="zh-CN" data-theme="light"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"><title>JS的作用域和this | Brilliance02</title><meta name="keywords" content="JS"><meta name="author" content="张朝辉"><meta name="copyright" content="张朝辉"><meta name="format-detection" content="telephone=no"><meta name="theme-color" content="#ffffff"><meta name="description" content="JS的作用域和this">
<meta property="og:type" content="article">
<meta property="og:title" content="JS的作用域和this">
<meta property="og:url" content="http://brilliance02.gitee.io/2022/06/07/this/index.html">
<meta property="og:site_name" content="Brilliance02">
<meta property="og:description" content="JS的作用域和this">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="http://brilliance02.gitee.io/img/11.jpg">
<meta property="article:published_time" content="2022-06-07T02:29:50.000Z">
<meta property="article:modified_time" content="2022-06-07T02:51:26.800Z">
<meta property="article:author" content="张朝辉">
<meta property="article:tag" content="JS">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="http://brilliance02.gitee.io/img/11.jpg"><link rel="shortcut icon" href="/img/favicon.png"><link rel="canonical" href="http://brilliance02.gitee.io/2022/06/07/this/"><link rel="preconnect" href="//cdn.jsdelivr.net"/><link rel="preconnect" href="//busuanzi.ibruce.info"/><link rel="stylesheet" href="/css/index.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6/css/all.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.css" media="print" onload="this.media='all'"><script>const GLOBAL_CONFIG = { 
  root: '/',
  algolia: undefined,
  localSearch: undefined,
  translate: undefined,
  noticeOutdate: undefined,
  highlight: {"plugin":"highlighjs","highlightCopy":true,"highlightLang":true,"highlightHeightLimit":false},
  copy: {
    success: '复制成功',
    error: '复制错误',
    noSupport: '浏览器不支持'
  },
  relativeDate: {
    homepage: false,
    post: false
  },
  runtime: '',
  date_suffix: {
    just: '刚刚',
    min: '分钟前',
    hour: '小时前',
    day: '天前',
    month: '个月前'
  },
  copyright: undefined,
  lightbox: 'fancybox',
  Snackbar: undefined,
  source: {
    justifiedGallery: {
      js: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery@2/dist/fjGallery.min.js',
      css: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery@2/dist/fjGallery.min.css'
    }
  },
  isPhotoFigcaption: false,
  islazyload: false,
  isAnchor: false
}</script><script id="config-diff">var GLOBAL_CONFIG_SITE = {
  title: 'JS的作用域和this',
  isPost: true,
  isHome: false,
  isHighlightShrink: false,
  isToc: true,
  postUpdate: '2022-06-07 10:51:26'
}</script><noscript><style type="text/css">
  #nav {
    opacity: 1
  }
  .justified-gallery img {
    opacity: 1
  }

  #recent-posts time,
  #post-meta time {
    display: inline !important
  }
</style></noscript><script>(win=>{
    win.saveToLocal = {
      set: function setWithExpiry(key, value, ttl) {
        if (ttl === 0) return
        const now = new Date()
        const expiryDay = ttl * 86400000
        const item = {
          value: value,
          expiry: now.getTime() + expiryDay,
        }
        localStorage.setItem(key, JSON.stringify(item))
      },

      get: function getWithExpiry(key) {
        const itemStr = localStorage.getItem(key)

        if (!itemStr) {
          return undefined
        }
        const item = JSON.parse(itemStr)
        const now = new Date()

        if (now.getTime() > item.expiry) {
          localStorage.removeItem(key)
          return undefined
        }
        return item.value
      }
    }
  
    win.getScript = url => new Promise((resolve, reject) => {
      const script = document.createElement('script')
      script.src = url
      script.async = true
      script.onerror = reject
      script.onload = script.onreadystatechange = function() {
        const loadState = this.readyState
        if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
        script.onload = script.onreadystatechange = null
        resolve()
      }
      document.head.appendChild(script)
    })
  
      win.activateDarkMode = function () {
        document.documentElement.setAttribute('data-theme', 'dark')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#0d0d0d')
        }
      }
      win.activateLightMode = function () {
        document.documentElement.setAttribute('data-theme', 'light')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#ffffff')
        }
      }
      const t = saveToLocal.get('theme')
    
          if (t === 'dark') activateDarkMode()
          else if (t === 'light') activateLightMode()
        
      const asideStatus = saveToLocal.get('aside-status')
      if (asideStatus !== undefined) {
        if (asideStatus === 'hide') {
          document.documentElement.classList.add('hide-aside')
        } else {
          document.documentElement.classList.remove('hide-aside')
        }
      }
    
    const detectApple = () => {
      if(/iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent)){
        document.documentElement.classList.add('apple')
      }
    }
    detectApple()
    })(window)</script><meta name="generator" content="Hexo 6.2.0"></head><body><div id="sidebar"><div id="menu-mask"></div><div id="sidebar-menus"><div class="avatar-img is-center"><img src="/img/1.jpg" onerror="onerror=null;src='/img/friend_404.gif'" alt="avatar"/></div><div class="sidebar-site-data site-data is-center"><a href="/archives/"><div class="headline">文章</div><div class="length-num">4</div></a><a href="/tags/"><div class="headline">标签</div><div class="length-num">3</div></a><a href="/categories/"><div class="headline">分类</div><div class="length-num">2</div></a></div><hr/><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 主页</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fas fa-archive"></i><span> 时间轴</span></a></div><div class="menus_item"><a class="site-page" href="/link/"><i class="fa-fw fas fa-link"></i><span> 链接</span></a></div><div class="menus_item"><a class="site-page" href="/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page group" href="javascript:void(0);"><i class="fa-fw fas fa-list"></i><span> 生活</span><i class="fas fa-chevron-down"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/photos/"><i class="fa-fw fa fa-camera-retro"></i><span> 相册</span></a></li><li><a class="site-page child" href="/music/"><i class="fa-fw fa fa-music"></i><span> 音乐</span></a></li></ul></div><div class="menus_item"><a class="site-page" href="/comment/"><i class="fa-fw fa fa-paper-plane"></i><span> 留言板</span></a></div><div class="menus_item"><a class="site-page" href="/about/"><i class="fa-fw fas fa-heart"></i><span> 关于</span></a></div></div></div></div><div class="post" id="body-wrap"><header class="post-bg" id="page-header" style="background-image: url('/img/11.jpg')"><nav id="nav"><span id="blog_name"><a id="site-name" href="/">Brilliance02</a></span><div id="menus"><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 主页</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fas fa-archive"></i><span> 时间轴</span></a></div><div class="menus_item"><a class="site-page" href="/link/"><i class="fa-fw fas fa-link"></i><span> 链接</span></a></div><div class="menus_item"><a class="site-page" href="/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page group" href="javascript:void(0);"><i class="fa-fw fas fa-list"></i><span> 生活</span><i class="fas fa-chevron-down"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/photos/"><i class="fa-fw fa fa-camera-retro"></i><span> 相册</span></a></li><li><a class="site-page child" href="/music/"><i class="fa-fw fa fa-music"></i><span> 音乐</span></a></li></ul></div><div class="menus_item"><a class="site-page" href="/comment/"><i class="fa-fw fa fa-paper-plane"></i><span> 留言板</span></a></div><div class="menus_item"><a class="site-page" href="/about/"><i class="fa-fw fas fa-heart"></i><span> 关于</span></a></div></div><div id="toggle-menu"><a class="site-page"><i class="fas fa-bars fa-fw"></i></a></div></div></nav><div id="post-info"><h1 class="post-title">JS的作用域和this</h1><div id="post-meta"><div class="meta-firstline"><span class="post-meta-date"><i class="far fa-calendar-alt fa-fw post-meta-icon"></i><span class="post-meta-label">发表于</span><time class="post-meta-date-created" datetime="2022-06-07T02:29:50.000Z" title="发表于 2022-06-07 10:29:50">2022-06-07</time><span class="post-meta-separator">|</span><i class="fas fa-history fa-fw post-meta-icon"></i><span class="post-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2022-06-07T02:51:26.800Z" title="更新于 2022-06-07 10:51:26">2022-06-07</time></span><span class="post-meta-categories"><span class="post-meta-separator">|</span><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/categories/JS%E6%8F%90%E9%AB%98/">JS提高</a></span></div><div class="meta-secondline"><span class="post-meta-separator">|</span><span class="post-meta-wordcount"><i class="far fa-file-word fa-fw post-meta-icon"></i><span class="post-meta-label">字数总计:</span><span class="word-count">5.4k</span><span class="post-meta-separator">|</span><i class="far fa-clock fa-fw post-meta-icon"></i><span class="post-meta-label">阅读时长:</span><span>18分钟</span></span><span class="post-meta-separator">|</span><span class="post-meta-pv-cv" id="" data-flag-title="JS的作用域和this"><i class="far fa-eye fa-fw post-meta-icon"></i><span class="post-meta-label">阅读量:</span><span id="busuanzi_value_page_pv"></span></span></div></div></div></header><main class="layout" id="content-inner"><div id="post"><article class="post-content" id="article-container"><h1 id="作用域"><a href="#作用域" class="headerlink" title="作用域"></a>作用域</h1><p><strong>作用域是根据名称查找变量的一套规则</strong></p>
<p>作用域共有两种主要的工作模型。第一种是最为普遍的，被大多数编程语言所采用的<strong>词法</strong><br><strong>作用域</strong>，我们会对这种作用域进行深入讨论。另外一种叫作<strong>动态作用域</strong>，仍有一些编程语<br>言在使用（比如 Bash 脚本、Perl 中的一些模式等）。</p>
<h2 id="LHS与RHS"><a href="#LHS与RHS" class="headerlink" title="LHS与RHS"></a>LHS与RHS</h2><p><strong>LHS 查询</strong>：当变量出现在赋值操作的左侧时。LHS 查询是试图找到变量的容器本身，从而可以对其赋值。赋值操作的目标是谁（LHS）</p>
<p><strong>RHS 查询</strong>：出现在右侧时，RHS 查询与简单地查找某个变量的值别无二致。谁是赋值操作的源头（RHS）</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">foo</span>(<span class="params">a</span>) &#123;</span><br><span class="line">	<span class="variable language_">console</span>.<span class="title function_">log</span>( a ); <span class="comment">// 2</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="title function_">foo</span>( <span class="number">2</span> );</span><br></pre></td></tr></table></figure>

<ul>
<li>foo(..) 函数的调用需要对 foo 进行 RHS 引用，意味着“去找到 foo 的值，并把它给我”</li>
<li>隐式的 a＝2 操作,LHS 查询</li>
<li>对 a 进行的 RHS 引用，并且将得到的值传给了 console.log(..) 。</li>
<li>console.log(..) 本身也需要一个引用才能执行，因此会对 console 对象进行 RHS 查询，并且检查得到的值中是否有一个叫作 log 的方法。</li>
</ul>
<p>如果查找的目的是对变量进行赋值，那么就会使用 LHS 查询；如果目的是获取变量值，就会使用 RHS 查询。</p>
<h2 id="作用域嵌套"><a href="#作用域嵌套" class="headerlink" title="作用域嵌套"></a>作用域嵌套</h2><p>当一个块或函数嵌套在另一个块或函数中时，就发生了作用域的嵌套。因此，在当前作用<br>域中无法找到某个变量时，引擎就会在外层嵌套的作用域中继续查找，直到找到该变量，<br>或抵达最外层的作用域（也就是全局作用域）为止。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">foo</span>(<span class="params">a</span>) &#123;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>( a + b );</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> b = <span class="number">2</span>;</span><br><span class="line"><span class="title function_">foo</span>( <span class="number">2</span> ); <span class="comment">// 4</span></span><br></pre></td></tr></table></figure>

<p>遍历嵌套作用域链的规则很简单：引擎从当前的执行作用域开始查找变量，如果找不到，<br>就向上一级继续查找。当抵达最外层的全局作用域时，无论找到还是没找到，查找过程都<br>会停止。</p>
<h2 id="异常"><a href="#异常" class="headerlink" title="异常"></a>异常</h2><p> <strong>RHS 查询</strong>在所有嵌套的作用域中遍寻不到所需的变量，引擎就会抛出 ReferenceError异常。值得注意的是， ReferenceError 是非常重要的异常类型。</p>
<p>当引擎执行 <strong>LHS 查询</strong>时，如果在顶层（全局作用域）中也无法找到目标变量，全局作用域中就会<u>创建一个具有该名称的变量</u>，并将其返还给引擎，前提是程序运行在<strong>非“严格模式”</strong>下。</p>
<h2 id="“严格模式”"><a href="#“严格模式”" class="headerlink" title="“严格模式”"></a>“严格模式”</h2><p>同正常模式，或者说宽松 &#x2F; 懒惰模式相比，严格模式在行为上有很多不同。其中一个不同的行为是<strong>严格模式禁止自动或隐式地创建全局变量</strong>。因此，在严格模式中 LHS 查询失败时，并不会创建并返回一个全局变量，引擎会抛出同 RHS 查询失败时的ReferenceError 异常。</p>
<p>接下来，如果 RHS 查询找到了一个变量，但是你尝试对这个变量的值进行<strong>不合理的操作</strong>，比如试图对一个非函数类型的值进行函数调用，或着引用 null 或 undefined 类型的值中的属性，那么引擎会抛出另外一种类型的异常，叫作 <strong>TypeError</strong> 。</p>
<p><strong>ReferenceError 同作用域判别失败相关，而 TypeError 则代表作用域判别成功了，但是对结果的操作是非法或不合理的。</strong></p>
<h2 id="词法作用域"><a href="#词法作用域" class="headerlink" title="词法作用域"></a>词法作用域</h2><p>词法作用域就是定义在词法阶段的作用域。换句话说，<strong>词法作用域是由你在写代码时将变量和块作用域写在哪里来决定的</strong>，因此当词法分析器处理代码时会<strong>保持作用域不变</strong>（大部分情况下是这样的）。</p>
<p>无论函数在哪里被调用，也无论它如何被调用，它的<strong>词法作用域都只由函数被声明时所处</strong><br><strong>的位置决定</strong>。</p>
<p><strong>词法作用域查找只会查找一级标识符</strong>，比如 a、b 和 c 。如果代码中引用了 foo.bar.baz ，词法作用域查找只会试图查找 foo 标识符，找到这个变量后，<strong>对象属性访问规则</strong>会分别接管对 bar 和 baz 属性的访问。</p>
<h3 id="遮蔽效应"><a href="#遮蔽效应" class="headerlink" title="遮蔽效应"></a>遮蔽效应</h3><p><strong>作用域查找会在找到第一个匹配的标识符时停止</strong>。在多层的嵌套作用域中可以定义同名的<br>标识符，这叫作<strong>“遮蔽效应</strong>”（内部的标识符“遮蔽”了外部的标识符）。抛开遮蔽效应，作用域查找始终从运行时所处的最内部作用域开始，逐级向外或者说向上进行，直到遇见<br>第一个匹配的标识符为止。</p>
<p>全局变量会自动成为全局对象（比如浏览器中的 window 对象）的属性，因此可以不直接通过全局对象的词法名称，而是间接地通过对全局对象属性的引用来对其进行访问。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">window</span>.<span class="property">a</span></span><br></pre></td></tr></table></figure>

<p>通过这种技术可以访问那些<strong>被同名变量所遮蔽的全局变量</strong>。但<strong>非全局的变量如果被遮蔽了，无论如何都无法被访问到</strong>。</p>
<h3 id="欺骗词法"><a href="#欺骗词法" class="headerlink" title="欺骗词法"></a>欺骗词法</h3><p>怎样才能在运行时来“修改”（也可以说欺骗）词法作用域呢？</p>
<p>欺骗词法作用域会导致性能下降</p>
<p><strong>eval</strong><br>JavaScript 中的 eval(..) 函数可以接受一个字符串为参数，并将<strong>其中的内容视为好像在书写时就存在于程序中这个位置的代码</strong>。换句话说，可以在你写的代码中用程序生成代码并<br>运行，就好像代码是写在那个位置的一样。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">foo</span>(<span class="params">str, a</span>) &#123;</span><br><span class="line"><span class="built_in">eval</span>( str ); <span class="comment">// 欺骗！</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>( a, b );</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> b = <span class="number">2</span>;</span><br><span class="line"><span class="title function_">foo</span>( <span class="string">&quot;var b = 3;&quot;</span>, <span class="number">1</span> ); <span class="comment">// 1, 3</span></span><br></pre></td></tr></table></figure>

<ul>
<li>eval(..) 调用中的 “var b &#x3D; 3;” 这段代码会被当作本来就在那里一样来处理。</li>
<li>这段代码实际上在 foo(..) 内部创建了一个变量 b ，并遮蔽了外部（全局）作用域中的同名变量。</li>
</ul>
<p>在严格模式的程序中， eval(..) 在运行时有其自己的词法作用域，意味着其中的声明无法修改所在的作用域。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">foo</span>(<span class="params">str</span>) &#123;</span><br><span class="line">	<span class="string">&quot;use strict&quot;</span>;</span><br><span class="line">	<span class="built_in">eval</span>( str );</span><br><span class="line">	<span class="variable language_">console</span>.<span class="title function_">log</span>( a ); <span class="comment">// ReferenceError: a is not defined</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="title function_">foo</span>( <span class="string">&quot;var a = 2&quot;</span> );</span><br></pre></td></tr></table></figure>

<p>它所带来的好处无法抵消性能上的损失。</p>
<p> <strong>with</strong></p>
<p>JavaScript 中另一个难以掌握（并且现在也不推荐使用）的用来欺骗词法作用域的功能是<br>with 关键字。</p>
<p>with 通常被当作重复引用同一个对象中的多个属性的快捷方式，可以不需要重复引用对象本身。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> obj = &#123;</span><br><span class="line"><span class="attr">a</span>: <span class="number">1</span>,</span><br><span class="line"><span class="attr">b</span>: <span class="number">2</span>,</span><br><span class="line"><span class="attr">c</span>: <span class="number">3</span></span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">// 单调乏味的重复 &quot;obj&quot;</span></span><br><span class="line">obj.<span class="property">a</span> = <span class="number">2</span>;</span><br><span class="line">obj.<span class="property">b</span> = <span class="number">3</span>;</span><br><span class="line">obj.<span class="property">c</span> = <span class="number">4</span>;</span><br><span class="line"><span class="comment">// 简单的快捷方式</span></span><br><span class="line"><span class="keyword">with</span> (obj) &#123;</span><br><span class="line">a = <span class="number">3</span>;</span><br><span class="line">b = <span class="number">4</span>;</span><br><span class="line">c = <span class="number">5</span>;</span><br><span class="line">&#125;</span><br><span class="line">------------------------------------</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">foo</span>(<span class="params">obj</span>) &#123;</span><br><span class="line"><span class="keyword">with</span> (obj) &#123;</span><br><span class="line">a = <span class="number">2</span>;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> o1 = &#123;</span><br><span class="line"><span class="attr">a</span>: <span class="number">3</span></span><br><span class="line">&#125;;</span><br><span class="line"><span class="keyword">var</span> o2 = &#123;</span><br><span class="line"><span class="attr">b</span>: <span class="number">3</span></span><br><span class="line">&#125;;</span><br><span class="line"><span class="title function_">foo</span>( o1 );<span class="comment">//当我们将 o1 传递进去， a＝2 赋值操作找到了 o1.a 并将 2 赋值给它</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>( o1.<span class="property">a</span> ); <span class="comment">// 2</span></span><br><span class="line"><span class="title function_">foo</span>( o2 );<span class="comment">//当 o2 传递进去， o2 并没有 a 属性，因此不会创建这个属性，o2.a 保持 undefined 。</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>( o2.<span class="property">a</span> ); <span class="comment">// undefined</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>( a ); <span class="comment">// 2——不好，a 被泄漏到全局作用域上了！</span></span><br></pre></td></tr></table></figure>

<p>with 可以将一个没有或有多个属性的对象处理为一个完全隔离的词法作用域，因此这个对象的属性也会被处理为定义在这个作用域中的词法标识符。</p>
<p>尽管 with 块可以将一个对象处理为词法作用域，但是这个块内部正常的 var声明并不会被限制在这个块的作用域中，而是被添加到 with 所处的函数作用域中。</p>
<p>with 声明实际上是根据你传递给它的对象凭空创建了一个全新的词法作用域。</p>
<p>eval(..) 和 with 会在运行时修改或创建新的作用域，以此来欺骗其他在书写时定义的词法作用域。</p>
<p>不要使用它们</p>
<h2 id="函数作用域和块作用域"><a href="#函数作用域和块作用域" class="headerlink" title="函数作用域和块作用域"></a>函数作用域和块作用域</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">foo</span>(<span class="params">a</span>) &#123;</span><br><span class="line">	<span class="keyword">var</span> b = <span class="number">2</span>;</span><br><span class="line">	<span class="comment">// 一些代码</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">bar</span>(<span class="params"></span>) &#123;</span><br><span class="line">	<span class="comment">// ...</span></span><br><span class="line">	&#125;</span><br><span class="line">	<span class="comment">// 更多的代码</span></span><br><span class="line">	<span class="keyword">var</span> c = <span class="number">3</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>无论标识符声明出现在作用域中的何处，这个标识符所代表的变量或函数都将附属于所处作用域的气泡。</p>
<p>由于标识符 a 、b 、c 和 bar 都附属于 foo(..) 的作用域气泡，因此无法从 foo(..) 的外部<br>对它们进行访问。也就是说，这些标识符全都无法从全局作用域中进行访问</p>
<p><code>bar(); // 失败</code> <code>console.log( a, b, c ); // 三个全都失败</code>会导致ReferenceError 错误</p>
<p>函数作用域的含义是指，<strong>属于这个函数的全部变量都可以在整个函数的范围内使用及复</strong><br><strong>用（事实上在嵌套的作用域中也可以使用）</strong>。</p>
<h3 id="隐藏内部实现"><a href="#隐藏内部实现" class="headerlink" title="隐藏内部实现"></a>隐藏内部实现</h3><p>从所写的代码中挑选出一个任意的片段，然后用函数声明对它进行包装，实际上就是把这些代码“隐藏”起来了。</p>
<p>可以把变量和函数包裹在一个函数的作用域中，然后用这个作用域来“隐藏”它们。</p>
<p>为什么“隐藏”变量和函数是一个有用的技术？</p>
<p><strong>最小特权原则</strong>（最小授权或最小暴露原则）这个原则是指在软件设计中，应该最小限度地暴露必要内容，而将其他内容都“隐藏”起来。</p>
<p><strong>规避冲突</strong></p>
<p>“隐藏”作用域中的变量和函数所带来的另一个好处，是可以避免同名标识符之间的冲突，<br>两个标识符可能具有相同的名字但用途却不一样，无意间可能造成命名冲突。冲突会导致<br>变量的值被意外覆盖。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">foo</span>(<span class="params"></span>) &#123;</span><br><span class="line">	<span class="keyword">function</span> <span class="title function_">bar</span>(<span class="params">a</span>) &#123;</span><br><span class="line">		i = <span class="number">3</span>; <span class="comment">// 修改 for 循环所属作用域中的 i</span></span><br><span class="line">		<span class="variable language_">console</span>.<span class="title function_">log</span>( a + i );</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">for</span> (<span class="keyword">var</span> i=<span class="number">0</span>; i&lt;<span class="number">10</span>; i++) &#123;</span><br><span class="line">		<span class="title function_">bar</span>( i * <span class="number">2</span> ); <span class="comment">// 糟糕，无限循环了！</span></span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="title function_">foo</span>();</span><br></pre></td></tr></table></figure>

<p>bar(..) 内部的赋值表达式 i &#x3D; 3 意外地覆盖了声明在 foo(..) 内部 for 循环中的 i 。在这<br>个例子中将会导致无限循环，因为 i 被固定设置为 3，永远满足小于 10 这个条件。</p>
<ul>
<li>bar(..) 内部的赋值操作需要声明一个本地变量来使用，采用任何名字都可以， var i &#x3D; 3;就可以满足这个需求（同时会为 i 声明一个前面提到过的“遮蔽变量”）。</li>
<li>另外一种方法是采用一个完全不同的标识符名称，比如 var j &#x3D; 3;</li>
</ul>
<p><strong>全局命名空间</strong><br>变量冲突的一个典型例子存在于全局作用域中。当程序中加载了多个第三方库时，如果它<br>们没有妥善地将内部私有的函数或变量隐藏起来，就会很容易引发冲突。</p>
<p>这些库通常会<strong>在全局作用域中声明一个名字足够独特的变量</strong>，通常是一个<strong>对象</strong>。这个对象<br>被用作库的命名空间，<strong>所有需要暴露给外界的功能都会成为这个对象（命名空间）的属</strong><br><strong>性</strong>，而不是将自己的标识符暴漏在顶级的词法作用域中。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> <span class="title class_">MyReallyCoolLibrary</span> = &#123;</span><br><span class="line">	<span class="attr">awesome</span>: <span class="string">&quot;stuff&quot;</span>,</span><br><span class="line">	<span class="attr">doSomething</span>: <span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">	<span class="comment">// ...</span></span><br><span class="line">&#125;,</span><br><span class="line"><span class="attr">doAnotherThing</span>: <span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">	<span class="comment">// ...</span></span><br><span class="line">	&#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p><strong>模块管理</strong></p>
<p>另外一种避免冲突的办法和现代的模块机制很接近，就是<strong>从众多模块管理器中挑选一个来</strong><br><strong>使用</strong>。使用这些工具，任何库都无需将标识符加入到全局作用域中，而是<strong>通过依赖管理器</strong><br><strong>的机制将库的标识符显式地导入到另外一个特定的作用域中</strong>。</p>
<p>显而易见，这些工具并没有能够违反词法作用域规则的“神奇”功能。它们只是利用作用域的规则<strong>强制所有标识符都不能注入到共享作用域中，而是保持在私有、无冲突的作用域中</strong>，这样可以有效规避掉所有的意外冲突。<br>因此，只要你愿意，即使不使用任何依赖管理工具也可以实现相同的功效</p>
<h3 id="函数作用域"><a href="#函数作用域" class="headerlink" title="函数作用域"></a>函数作用域</h3><p>在任意代码片段外部添加包装函数，可以将内部的变量和函数定义“隐藏”起来，外部作用域无法访问包装函数内部的任何内容。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = <span class="number">2</span>;</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">foo</span>(<span class="params"></span>) &#123; <span class="comment">// &lt;-- 添加这一行</span></span><br><span class="line">	<span class="keyword">var</span> a = <span class="number">3</span>;</span><br><span class="line">	<span class="variable language_">console</span>.<span class="title function_">log</span>( a ); <span class="comment">// 3</span></span><br><span class="line">&#125; <span class="comment">// &lt;-- 以及这一行</span></span><br><span class="line"><span class="title function_">foo</span>(); <span class="comment">// &lt;-- 以及这一行</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>( a ); <span class="comment">// 2</span></span><br></pre></td></tr></table></figure>

<p>会导致一些额外的问题</p>
<ul>
<li>首先，必须声明一个具名函数 foo() ，意味着 foo 这个名称本身“污染”了所在作用域（在这个例子中是全局作用域）。</li>
<li>其次，必须显式地通过函数名（ foo() ）调用这个函数才能运行其中的代码。</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = <span class="number">2</span>;</span><br><span class="line">( <span class="keyword">function</span> <span class="title function_">foo</span>(<span class="params"></span>)&#123; <span class="comment">// &lt;-- 添加这一行</span></span><br><span class="line">	<span class="keyword">var</span> a = <span class="number">3</span>;</span><br><span class="line">	<span class="variable language_">console</span>.<span class="title function_">log</span>( a ); <span class="comment">// 3</span></span><br><span class="line">&#125;)(); <span class="comment">// &lt;-- 以及这一行</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>( a ); <span class="comment">// 2</span></span><br></pre></td></tr></table></figure>

<p>包装函数的声明以 (function… 而不仅是以 function… 开始。尽管看上去这并不是一个很显眼的细节，但实际上却是非常重要的区别。函数会被当作函数表达式而不是一个标准的函数声明来处理。</p>
<blockquote>
<p>区分函数声明和表达式最简单的方法是看 function 关键字出现在声明中的位置（不仅仅是一行代码，而是整个声明中的位置）。如果 function 是声明中的第一个词，那么就是一个函数声明，否则就是一个函数表达式。</p>
</blockquote>
<p>函数声明和函数表达式之间最重要的区别是它们的名称标识符将会绑定在何处。</p>
<p>第一个片段中 foo 被绑定在所在作用域中，可以直接通过foo() 来调用它。第二个片段中 foo 被绑定在函数表达式自身的函数中而不是所在作用域中。</p>
<p> (function foo(){ .. }) 作为函数表达式意味着 foo 只能在 .. 所代表的位置中被访问，外部作用域则不行。 foo 变量名被隐藏在自身中意味着不会非必要地污染外部作用域。</p>
<p>函数表达式：函数名被绑定在自己的函数作用域中，只能在自己的函数体内调用</p>
<h3 id="匿名和具名"><a href="#匿名和具名" class="headerlink" title="匿名和具名"></a>匿名和具名</h3><p>函数表达式可以是匿名的，而函数声明则不可以省略函数名——在 JavaScript 的语法中这是非法的。</p>
<p>几个缺点</p>
<ul>
<li>匿名函数在栈追踪中不会显示出有意义的函数名，使得调试很困难。</li>
<li>如果没有函数名，当函数需要引用自身时只能使用已经过期的 arguments.callee 引用，比如在递归中。另一个函数需要引用自身的例子，是在事件触发后事件监听器需要解绑自身。</li>
<li>匿名函数省略了对于代码可读性 &#x2F; 可理解性很重要的函数名。一个描述性的名称可以让代码不言自明。</li>
</ul>
<h3 id="立即执行函数表达式"><a href="#立即执行函数表达式" class="headerlink" title="立即执行函数表达式"></a>立即执行函数表达式</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = <span class="number">2</span>;</span><br><span class="line">(<span class="keyword">function</span> <span class="title function_">foo</span>(<span class="params"></span>) &#123;</span><br><span class="line"><span class="keyword">var</span> a = <span class="number">3</span>;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>( a ); <span class="comment">// 3</span></span><br><span class="line">&#125;)();</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>( a ); <span class="comment">// 2</span></span><br></pre></td></tr></table></figure>

<p><strong>IIFE</strong>，代表立即执行函数表达式</p>
<p>函数被包含在一对 ( ) 括号内部，因此成为了一个表达式，通过在末尾加上另外一个( ) 可以立即执行这个函数，比如 (function foo(){ .. })() 。第一个 ( ) 将函数变成表达式，第二个 ( ) 执行了这个函数。</p>
<p>另一个改进的形式： <strong>(function(){ .. }())</strong> </p>
<p>仔细观察其中的区别。第一种形式中函数表达式被包含在 ( ) 中，然后在后面用另一个 () 括号来调用。第二种形式中用来调用的 () 括号被移进了用来包装的 ( ) 括号中。</p>
<ul>
<li>IIFE 的另一个非常普遍的进阶用法是把它们当作函数调用并传递参数进去。</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = <span class="number">2</span>;</span><br><span class="line">(<span class="keyword">function</span> <span class="title function_">IIFE</span>(<span class="params"> <span class="variable language_">global</span> </span>) &#123;</span><br><span class="line"><span class="keyword">var</span> a = <span class="number">3</span>;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>( a ); <span class="comment">// 3</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>( <span class="variable language_">global</span>.<span class="property">a</span> ); <span class="comment">// 2</span></span><br><span class="line">&#125;)( <span class="variable language_">window</span> );<span class="comment">//将 window 对象的引用传递进去，但将参数命名为 global</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>( a ); <span class="comment">// 2</span></span><br></pre></td></tr></table></figure>

<ul>
<li><p>解决 undefined 标识符的默认值被错误覆盖导致的异常（虽然不常见）。将一个参数命名为 undefined ，但是在对应的位置不传入任何值，这样就可以保证在代码块中 undefined 标识符的值真的是 undefined ,</p>
<p>解决undefined被赋值的问题，使undefined就是undefined</p>
</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="literal">undefined</span> = <span class="literal">true</span>; <span class="comment">// 给其他代码挖了一个大坑！绝对不要这样做！</span></span><br><span class="line">(<span class="keyword">function</span> <span class="title function_">IIFE</span>(<span class="params"> <span class="literal">undefined</span> </span>) &#123;</span><br><span class="line"><span class="keyword">var</span> a;</span><br><span class="line"><span class="keyword">if</span> (a === <span class="literal">undefined</span>) &#123;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>( <span class="string">&quot;Undefined is safe here!&quot;</span> );</span><br><span class="line">&#125;</span><br><span class="line">&#125;)();</span><br></pre></td></tr></table></figure>

<ul>
<li>倒置代码的运行顺序，将需要运行的函数放在第二位，在 IIFE执行之后当作参数传递进去。</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = <span class="number">2</span>;</span><br><span class="line">(<span class="keyword">function</span> <span class="title function_">IIFE</span>(<span class="params"> def </span>) &#123;</span><br><span class="line">	<span class="title function_">def</span>( <span class="variable language_">window</span> );</span><br><span class="line">&#125;)(<span class="keyword">function</span> <span class="title function_">def</span>(<span class="params"> <span class="variable language_">global</span> </span>) &#123;</span><br><span class="line">	<span class="keyword">var</span> a = <span class="number">3</span>;</span><br><span class="line">	<span class="variable language_">console</span>.<span class="title function_">log</span>( a ); <span class="comment">// 3</span></span><br><span class="line">	<span class="variable language_">console</span>.<span class="title function_">log</span>( <span class="variable language_">global</span>.<span class="property">a</span> ); <span class="comment">// 2</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<p>函数表达式 def 定义在片段的第二部分，然后当作参数（这个参数也叫作 def ）被传递进<br>IIFE 函数定义的第一部分中。最后，参数 def （也就是传递进去的函数）被调用，并将<br>window 传入当作 global 参数的值。</p>
<p>把def函数定义后并传进立即调用函数中在IIFE中调用def并给def传参window</p>
<h2 id="块作用域"><a href="#块作用域" class="headerlink" title="块作用域"></a>块作用域</h2><h1 id="this"><a href="#this" class="headerlink" title="this"></a>this</h1><p><strong>对于默认绑定来说，决定 this 绑定对象的并不是调用位置是否处于严格模式，而是</strong><br><strong>函数体是否处于严格模式。如果函数体处于严格模式， this 会被绑定到 undefined ，否则</strong><br><strong>this 会被绑定到全局对象。</strong></p>
<h6 id="call和apply的区别"><a href="#call和apply的区别" class="headerlink" title="call和apply的区别"></a>call和apply的区别</h6><p>apply和call的功能是一样的，不同点就是<strong>接收参数的方式不同</strong></p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*apply()方法*/</span></span><br><span class="line"><span class="keyword">function</span>.<span class="title function_">apply</span>(thisObj[, argArray])</span><br><span class="line"></span><br><span class="line"><span class="comment">/*call()方法*/</span></span><br><span class="line"><span class="keyword">function</span>.<span class="title function_">call</span>(thisObj[, arg1[, arg2[, [,...argN]]]]);</span><br></pre></td></tr></table></figure>

<ul>
<li><p>apply：最多有两个参数——新this对象和<strong>一个参数数组Array</strong>。如果给该方法传递多个参数，则把参数都写进这个数组里面，即使只有一个参数，也要写进数组里。如果Array不是一个有效的数组或arguments对象，那么将导致一个TypeError。如果没有提供Array和thisObj任何一个参数，那么Global对象将被用作thisObj，并且无法被传递任何参数。</p>
</li>
<li><p>call：可以接受多个参数，第一个参数是新this对象，后面是<strong>一串参数列表</strong>。如果没有提供thisObj参数，那么 Global 对象被用作thisObj。</p>
</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">add</span>(<span class="params">a,b</span>)&#123;</span><br><span class="line">    <span class="keyword">var</span> c=a+b;</span><br><span class="line">  <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">str</span>+c;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> obj=&#123;</span><br><span class="line">    <span class="attr">str</span>:<span class="string">&quot;a+b=&quot;</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> q1=add.<span class="title function_">apply</span>(obj,[<span class="number">1</span>,<span class="number">2</span>]);</span><br><span class="line"><span class="keyword">var</span> q2=add.<span class="title function_">call</span>(obj,<span class="number">2</span>,<span class="number">2</span>);</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(q1);<span class="comment">//输出:a+b=3</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(q2);<span class="comment">//输出:a+b=4</span></span><br></pre></td></tr></table></figure>

<h6 id="bind函数的简单实现"><a href="#bind函数的简单实现" class="headerlink" title="bind函数的简单实现"></a>bind函数的简单实现</h6><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="title class_">Function</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">newbind</span> = <span class="keyword">function</span> (<span class="params">obj</span>) &#123;</span><br><span class="line">    <span class="keyword">const</span> self = <span class="variable language_">this</span>;    </span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">function</span> (<span class="params"></span>) &#123;</span><br><span class="line">        self.<span class="title function_">apply</span>(obj)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">bar.<span class="title function_">newbind</span>(obj1);</span><br></pre></td></tr></table></figure>

<h6 id="了解函数柯里化"><a href="#了解函数柯里化" class="headerlink" title="了解函数柯里化"></a>了解函数柯里化</h6><p><strong>柯里化是指一个函数(假设叫做createCurry)，他接收函数A作为参数，运行后能够返回一个新的函数。并且这个新的函数能够处理函数A的剩余参数。</strong></p>
<p>函数柯里化思想：一个JS<a target="_blank" rel="noopener" href="https://so.csdn.net/so/search?q=%E9%A2%84%E5%A4%84%E7%90%86&spm=1001.2101.3001.7020">预处理</a>的思想，降低通用性，提高适用性。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//普通函数</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">fn</span>(<span class="params">a,b,c,d,e</span>) &#123;</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(a,b,c,d,e)</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//生成的柯里化函数</span></span><br><span class="line"><span class="keyword">let</span> _fn = <span class="title function_">curry</span>(fn);</span><br><span class="line"></span><br><span class="line"><span class="title function_">_fn</span>(<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>);     <span class="comment">// print: 1,2,3,4,5</span></span><br><span class="line"><span class="title function_">_fn</span>(<span class="number">1</span>)(<span class="number">2</span>)(<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>);   <span class="comment">// print: 1,2,3,4,5</span></span><br><span class="line"><span class="title function_">_fn</span>(<span class="number">1</span>,<span class="number">2</span>)(<span class="number">3</span>,<span class="number">4</span>)(<span class="number">5</span>);   <span class="comment">// print: 1,2,3,4,5</span></span><br><span class="line"><span class="title function_">_fn</span>(<span class="number">1</span>)(<span class="number">2</span>)(<span class="number">3</span>)(<span class="number">4</span>)(<span class="number">5</span>); <span class="comment">// print: 1,2,3,4,5</span></span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>对于已经柯里化后的 _fn 函数来说，当接收的参数数量与原函数的形参数量相同时，执行原函数； 当接收的参数数量小于原函数的形参数量时，返回一个函数用于接收剩余参数的函数，直至接收的参数数量与形参数量一致，执行原函数。</p>
<p>柯里化实际是把简答的问题复杂化了，但是复杂化的同时，我们在使用函数时拥有了更加多的自由度。 而这里对于函数参数的自由处理，正是柯里化的核心所在。 柯里化本质上是降低通用性，提高适用性。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">checkByRegExp</span>(<span class="params">regExp,string</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> regExp.<span class="title function_">test</span>(string);  </span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="title function_">checkByRegExp</span>(<span class="regexp">/^1\d&#123;10&#125;$/</span>, <span class="string">&#x27;18642838455&#x27;</span>); <span class="comment">// 校验电话号码</span></span><br><span class="line"><span class="title function_">checkByRegExp</span>(<span class="regexp">/^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/</span>, <span class="string">&#x27;test@163.com&#x27;</span>); <span class="comment">// 校验邮箱</span></span><br><span class="line">--------------------------------------------------------------------------------------</span><br><span class="line"><span class="comment">//进行柯里化</span></span><br><span class="line"><span class="keyword">let</span> _check = <span class="title function_">curry</span>(checkByRegExp);</span><br><span class="line"><span class="comment">//生成工具函数，验证电话号码</span></span><br><span class="line"><span class="keyword">let</span> checkCellPhone = <span class="title function_">_check</span>(<span class="regexp">/^1\d&#123;10&#125;$/</span>);<span class="comment">//先给函数传入了第一个参数（校验规则）</span></span><br><span class="line"><span class="comment">//生成工具函数，验证邮箱</span></span><br><span class="line"><span class="keyword">let</span> checkEmail = <span class="title function_">_check</span>(<span class="regexp">/^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/</span>);</span><br><span class="line"></span><br><span class="line"><span class="title function_">checkCellPhone</span>(<span class="string">&#x27;18642838455&#x27;</span>); <span class="comment">// 传入第二个参数，函数从此时开始执行（校验的电话号码）</span></span><br><span class="line"></span><br><span class="line"><span class="title function_">checkEmail</span>(<span class="string">&#x27;test@163.com&#x27;</span>); <span class="comment">// 校验邮箱</span></span><br></pre></td></tr></table></figure>

<p><strong>封装柯里化工具函数</strong>实现 curry 函数（</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 函数柯里化第一步</span></span><br><span class="line"><span class="comment"> *  fn    待柯里化的原函数</span></span><br><span class="line"><span class="comment"> *  len   所需的参数个数，默认为原函数的形参个数</span></span><br><span class="line"><span class="comment"> **/</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">curry</span>(<span class="params">fn,len=fn.length</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> _curry.<span class="title function_">call</span>(<span class="variable language_">this</span>,fn,len)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 中转函数</span></span><br><span class="line"><span class="comment"> * fn    待柯里化的原函数</span></span><br><span class="line"><span class="comment"> * len   所需的参数个数</span></span><br><span class="line"><span class="comment"> * args  已接收的参数列表</span></span><br><span class="line"><span class="comment"> **/</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">_curry</span>(<span class="params">fn,len,...args</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">function</span> (<span class="params">...params</span>) &#123;</span><br><span class="line">        <span class="keyword">let</span> _args = [...args,...params];</span><br><span class="line">        <span class="keyword">if</span>(_args.<span class="property">length</span> &gt;= len)&#123;</span><br><span class="line">            <span class="keyword">return</span> fn.<span class="title function_">apply</span>(<span class="variable language_">this</span>,_args);</span><br><span class="line">        &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">            <span class="keyword">return</span> _curry.<span class="title function_">call</span>(<span class="variable language_">this</span>,fn,len,..._args)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

</article><div class="post-copyright"><div class="post-copyright__author"><span class="post-copyright-meta">文章作者: </span><span class="post-copyright-info"><a href="http://brilliance02.gitee.io">张朝辉</a></span></div><div class="post-copyright__type"><span class="post-copyright-meta">文章链接: </span><span class="post-copyright-info"><a href="http://brilliance02.gitee.io/2022/06/07/this/">http://brilliance02.gitee.io/2022/06/07/this/</a></span></div><div class="post-copyright__notice"><span class="post-copyright-meta">版权声明: </span><span class="post-copyright-info">本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">CC BY-NC-SA 4.0</a> 许可协议。转载请注明来自 <a href="http://brilliance02.gitee.io" target="_blank">Brilliance02</a>！</span></div></div><div class="tag_share"><div class="post-meta__tag-list"><a class="post-meta__tags" href="/tags/JS/">JS</a></div><div class="post_share"><div class="social-share" data-image="/img/11.jpg" data-sites="facebook,twitter,wechat,weibo,qq"></div><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/social-share.js/dist/css/share.min.css" media="print" onload="this.media='all'"><script src="https://cdn.jsdelivr.net/gh/overtrue/share.js@master/dist/js/social-share.min.js" defer></script></div></div><nav class="pagination-post" id="pagination"><div class="prev-post pull-left"><a href="/2022/06/07/reg/"><img class="prev-cover" src="/img/7.jpg" onerror="onerror=null;src='/img/404.jpg'" alt="cover of previous post"><div class="pagination-info"><div class="label">上一篇</div><div class="prev_info">正则表达式</div></div></a></div><div class="next-post pull-right"><a href="/2022/06/07/article/"><img class="next-cover" src="/img/6.jpg" onerror="onerror=null;src='/img/404.jpg'" alt="cover of next post"><div class="pagination-info"><div class="label">下一篇</div><div class="next_info">回调函数和异步机制</div></div></a></div></nav><div class="relatedPosts"><div class="headline"><i class="fas fa-thumbs-up fa-fw"></i><span>相关推荐</span></div><div class="relatedPosts-list"><div><a href="/2022/06/07/article/" title="回调函数和异步机制"><img class="cover" src="/img/6.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2022-06-07</div><div class="title">回调函数和异步机制</div></div></a></div></div></div></div><div class="aside-content" id="aside-content"><div class="card-widget card-info"><div class="is-center"><div class="avatar-img"><img src="/img/1.jpg" onerror="this.onerror=null;this.src='/img/friend_404.gif'" alt="avatar"/></div><div class="author-info__name">张朝辉</div><div class="author-info__description">欢迎进入ZZH's Blog</div></div><div class="card-info-data site-data is-center"><a href="/archives/"><div class="headline">文章</div><div class="length-num">4</div></a><a href="/tags/"><div class="headline">标签</div><div class="length-num">3</div></a><a href="/categories/"><div class="headline">分类</div><div class="length-num">2</div></a></div><a id="card-info-btn" target="_blank" rel="noopener" href="https://github.com/xxxxxx"><i class="fab fa-github"></i><span>Follow Me</span></a></div><div class="sticky_layout"><div class="card-widget" id="card-toc"><div class="item-headline"><i class="fas fa-stream"></i><span>目录</span><span class="toc-percentage"></span></div><div class="toc-content"><ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#%E4%BD%9C%E7%94%A8%E5%9F%9F"><span class="toc-number">1.</span> <span class="toc-text">作用域</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#LHS%E4%B8%8ERHS"><span class="toc-number">1.1.</span> <span class="toc-text">LHS与RHS</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E4%BD%9C%E7%94%A8%E5%9F%9F%E5%B5%8C%E5%A5%97"><span class="toc-number">1.2.</span> <span class="toc-text">作用域嵌套</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%BC%82%E5%B8%B8"><span class="toc-number">1.3.</span> <span class="toc-text">异常</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E2%80%9C%E4%B8%A5%E6%A0%BC%E6%A8%A1%E5%BC%8F%E2%80%9D"><span class="toc-number">1.4.</span> <span class="toc-text">“严格模式”</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E8%AF%8D%E6%B3%95%E4%BD%9C%E7%94%A8%E5%9F%9F"><span class="toc-number">1.5.</span> <span class="toc-text">词法作用域</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E9%81%AE%E8%94%BD%E6%95%88%E5%BA%94"><span class="toc-number">1.5.1.</span> <span class="toc-text">遮蔽效应</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%AC%BA%E9%AA%97%E8%AF%8D%E6%B3%95"><span class="toc-number">1.5.2.</span> <span class="toc-text">欺骗词法</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%87%BD%E6%95%B0%E4%BD%9C%E7%94%A8%E5%9F%9F%E5%92%8C%E5%9D%97%E4%BD%9C%E7%94%A8%E5%9F%9F"><span class="toc-number">1.6.</span> <span class="toc-text">函数作用域和块作用域</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E9%9A%90%E8%97%8F%E5%86%85%E9%83%A8%E5%AE%9E%E7%8E%B0"><span class="toc-number">1.6.1.</span> <span class="toc-text">隐藏内部实现</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%87%BD%E6%95%B0%E4%BD%9C%E7%94%A8%E5%9F%9F"><span class="toc-number">1.6.2.</span> <span class="toc-text">函数作用域</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%8C%BF%E5%90%8D%E5%92%8C%E5%85%B7%E5%90%8D"><span class="toc-number">1.6.3.</span> <span class="toc-text">匿名和具名</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%AB%8B%E5%8D%B3%E6%89%A7%E8%A1%8C%E5%87%BD%E6%95%B0%E8%A1%A8%E8%BE%BE%E5%BC%8F"><span class="toc-number">1.6.4.</span> <span class="toc-text">立即执行函数表达式</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%9D%97%E4%BD%9C%E7%94%A8%E5%9F%9F"><span class="toc-number">1.7.</span> <span class="toc-text">块作用域</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#this"><span class="toc-number">2.</span> <span class="toc-text">this</span></a><ol class="toc-child"><li class="toc-item toc-level-6"><a class="toc-link" href="#call%E5%92%8Capply%E7%9A%84%E5%8C%BA%E5%88%AB"><span class="toc-number">2.0.0.0.0.1.</span> <span class="toc-text">call和apply的区别</span></a></li><li class="toc-item toc-level-6"><a class="toc-link" href="#bind%E5%87%BD%E6%95%B0%E7%9A%84%E7%AE%80%E5%8D%95%E5%AE%9E%E7%8E%B0"><span class="toc-number">2.0.0.0.0.2.</span> <span class="toc-text">bind函数的简单实现</span></a></li><li class="toc-item toc-level-6"><a class="toc-link" href="#%E4%BA%86%E8%A7%A3%E5%87%BD%E6%95%B0%E6%9F%AF%E9%87%8C%E5%8C%96"><span class="toc-number">2.0.0.0.0.3.</span> <span class="toc-text">了解函数柯里化</span></a></li></ol></li></ol></li></ol></li></ol></li></ol></li></ol></div></div><div class="card-widget card-recent-post"><div class="item-headline"><i class="fas fa-history"></i><span>最新文章</span></div><div class="aside-list"><div class="aside-list-item"><a class="thumbnail" href="/2023/04/19/Flex%E5%BC%B9%E6%80%A7%E7%9B%92/" title="Flex弹性盒"><img src="/img/14.jpg" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="Flex弹性盒"/></a><div class="content"><a class="title" href="/2023/04/19/Flex%E5%BC%B9%E6%80%A7%E7%9B%92/" title="Flex弹性盒">Flex弹性盒</a><time datetime="2023-04-19T13:08:37.586Z" title="发表于 2023-04-19 21:08:37">2023-04-19</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/2022/06/07/reg/" title="正则表达式"><img src="/img/7.jpg" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="正则表达式"/></a><div class="content"><a class="title" href="/2022/06/07/reg/" title="正则表达式">正则表达式</a><time datetime="2022-06-07T02:34:40.000Z" title="发表于 2022-06-07 10:34:40">2022-06-07</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/2022/06/07/this/" title="JS的作用域和this"><img src="/img/11.jpg" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="JS的作用域和this"/></a><div class="content"><a class="title" href="/2022/06/07/this/" title="JS的作用域和this">JS的作用域和this</a><time datetime="2022-06-07T02:29:50.000Z" title="发表于 2022-06-07 10:29:50">2022-06-07</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/2022/06/07/article/" title="回调函数和异步机制"><img src="/img/6.jpg" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="回调函数和异步机制"/></a><div class="content"><a class="title" href="/2022/06/07/article/" title="回调函数和异步机制">回调函数和异步机制</a><time datetime="2022-06-07T01:39:35.000Z" title="发表于 2022-06-07 09:39:35">2022-06-07</time></div></div></div></div></div></div></main><footer id="footer"><div id="footer-wrap"><div class="copyright">&copy;2022 - 2023 By 张朝辉</div><div class="framework-info"><span>框架 </span><a target="_blank" rel="noopener" href="https://hexo.io">Hexo</a><span class="footer-separator">|</span><span>主题 </span><a target="_blank" rel="noopener" href="https://github.com/jerryc127/hexo-theme-butterfly">Butterfly</a></div><div class="footer_custom_text">Hi, welcome to my blog !</div></div></footer></div><div id="rightside"><div id="rightside-config-hide"><button id="readmode" type="button" title="阅读模式"><i class="fas fa-book-open"></i></button><button id="darkmode" type="button" title="浅色和深色模式转换"><i class="fas fa-adjust"></i></button><button id="hide-aside-btn" type="button" title="单栏和双栏切换"><i class="fas fa-arrows-alt-h"></i></button></div><div id="rightside-config-show"><button id="rightside_config" type="button" title="设置"><i class="fas fa-cog fa-spin"></i></button><button class="close" id="mobile-toc-button" type="button" title="目录"><i class="fas fa-list-ul"></i></button><button id="go-up" type="button" title="回到顶部"><i class="fas fa-arrow-up"></i></button></div></div><div><script src="/js/utils.js"></script><script src="/js/main.js"></script><script src="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.umd.js"></script><div class="js-pjax"></div><canvas class="fireworks" mobile="false"></canvas><script src="https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/fireworks.min.js"></script><script defer="defer" id="fluttering_ribbon" mobile="false" src="https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/canvas-fluttering-ribbon.min.js"></script><script async data-pjax src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script></div></body></html>